<?php

namespace app\service\news;

use app\model\news\VnNewsModel;
use GuzzleHttp\Client;
use hunma\exception\ApiException;
use Illuminate\Support\Facades\DB as Db;
use support\Log;


class VnNewsService
{
    protected Client $http;

    public function __construct()
    {
        $this->http = new Client(['verify' => false, 'timeout' => 10]);
    }

    /**
     * 获取当天新闻（若缺失则尝试抓取）
     */
    public function getDaily(string $date = ''): array
    {
        $date = $date ?: date('Y-m-d');
        $list = VnNewsModel::getList(['fetch_date' => $date], ['publish_time' => 'DESC'], '*', 1, 5);
        if (count($list) >= 3) {
            return $list;
        }
        // 不足则尝试抓取
        try {
            $this->fetchDaily(5);
            $list = VnNewsModel::getList(['fetch_date' => $date], ['publish_time' => 'DESC'], '*', 1, 5);
            if (!empty($list)) {
                return $list;
            }
        } catch (\Throwable $e) {
            Log::channel('ApiResponse')->error('当日抓取失败，尝试回退前一天', [$e->getMessage()]);
        }
        // 回退到最近有数据的一天
        $row = Db::table('vn_news')->orderBy('fetch_date', 'DESC')->first();
        if ($row) {
            return VnNewsModel::getList(['fetch_date' => $row->fetch_date], ['publish_time' => 'DESC'], '*', 1, 5);
        }
        return [];
    }

    /**
     * 每日抓取并写库（含容错与回退）
     */
    public function fetchDaily(int $max = 5): array
    {
        $today = date('Y-m-d');
        // 已有就跳过
        $existingCount = VnNewsModel::getTotal(['fetch_date' => $today]);
        if ($existingCount >= 3) {
            return VnNewsModel::getList(['fetch_date' => $today], ['publish_time' => 'DESC'], '*', 1, $max);
        }

        $articles = [];
        // 主源：VietnamPlus 中文 RSS
        try {
            $articles = $this->fetchFromVietnamPlusRss(5);
        } catch (\Throwable $e) {
            Log::channel('ApiResponse')->error('VietnamPlus RSS抓取失败', [$e->getMessage()]);
        }

        // 若不足，回退：VietnamPlus 最新页面解析
        if (count($articles) < 3) {
            try {
                $fallback = $this->fetchFromVietnamPlusMostRecent($max);
                $articles = $this->mergeUnique($articles, $fallback);
            } catch (\Throwable $e) {
                Log::channel('ApiResponse')->error('VietnamPlus 页面抓取失败', [$e->getMessage()]);
            }
        }
        // 写库（去重按 source_url）
        $saved = [];
        foreach ($articles as $a) {
            if (empty($a['title']) || empty($a['content']) || empty($a['source_url'])) {
                continue;
            }

            // cover_image 上传到 Docspace OSS 服务
            if (!empty($a['cover_image'])) {
                try {
                    $doc = new \app\service\http\docspace\DocspaceApiClient();
                    $uploaded = $doc->uploadByUrl($a['cover_image']);
                    if (!empty($uploaded)) {
                        $a['cover_image'] = $uploaded;
                    }
                } catch (\Throwable $e) {
                    Log::channel('ApiResponse')->error('封面图上传失败', [$e->getMessage(), $a['cover_image']]);
                }
            }

            // 保存到数据库（去重）
            try {

                // 过滤内容
                $a['content'] = $this->filterContent($a['content']);

                $exists = VnNewsModel::getOne(['source_url' => $a['source_url']], ['id']);
                if ($exists) {
                    VnNewsModel::edit([
                        'id' => $exists['id'],
                        'title' => $a['title'],
                        'content' => $a['content'],
                        'publish_time' => $a['publish_time'] ?? null,
                        'source_name' => $a['source_name'] ?? 'VietnamPlus',
                        'cover_image' => $a['cover_image'] ?? null,
                        'images_json' => json_encode($a['images'] ?? [], JSON_UNESCAPED_UNICODE)
                    ], ['title', 'content', 'publish_time', 'source_name', 'cover_image', 'images_json']);
                } else {
                    VnNewsModel::add([
                        'title' => $a['title'],
                        'content' => $a['content'],
                        'source_url' => $a['source_url'],
                        'source_name' => $a['source_name'] ?? 'VietnamPlus',
                        'cover_image' => $a['cover_image'] ?? null,
                        'images_json' => json_encode($a['images'] ?? [], JSON_UNESCAPED_UNICODE),
                        'publish_time' => $a['publish_time'] ?? null,
                        'fetch_date' => $today,
                    ]);
                }
                $saved[] = $a;
            } catch (\Throwable $e) {
                Log::channel('ApiResponse')->error('保存新闻失败', [$e->getMessage(), $a['source_url'] ?? '']);
            }
        }

        // 返回当日数据
        return VnNewsModel::getList(['fetch_date' => $today], ['publish_time' => 'DESC'], '*', 1, $max);
    }

    /**
     * 主源：VietnamPlus 中文 RSS 抓取
     */
    protected function fetchFromVietnamPlusRss(int $max): array
    {
        $rssPage = 'https://zh.vietnamplus.vn/rss.html';
        $feeds = $this->discoverRssFeeds($rssPage);
        if (empty($feeds)) {
            throw new ApiException('未发现 VietnamPlus RSS');
        }
        $articles = [];
        foreach ($feeds as $feedUrl) {
            if (count($articles) >= $max) break;
            try {
                $resp = $this->http->get($feedUrl);
                $body = (string)$resp->getBody();
                $items = $this->parseRssItems($body);
                foreach ($items as $item) {
                    if (count($articles) >= $max) break;
                    $link = $item['link'] ?? '';
                    if (!$link) continue;
                    $detail = $this->fetchVietnamPlusArticle($link);
                    if (!$detail) continue;
                    $articles[] = [
                        'title' => $item['title'] ?? '',
                        'content' => $detail['content'] ?? '',
                        'source_url' => $link,
                        'source_name' => 'VietnamPlus',
                        'publish_time' => $item['pubDate'] ?? null,
                        'cover_image' => $detail['cover_image'] ?? '',
                        'images' => $detail['images'] ?? []
                    ];
                }
            } catch (\Throwable $e) {
                Log::channel('ApiResponse')->error('RSS源解析失败', [$feedUrl, $e->getMessage()]);
                continue;
            }
        }
        return $articles;
    }

    /**
     * 回退：VietnamPlus 最新页面抓取
     */
    protected function fetchFromVietnamPlusMostRecent(int $max): array
    {
        $url = 'https://zh.vietnamplus.vn/mostrecent.vnp';
        $resp = $this->http->get($url);
        $html = (string)$resp->getBody();
        $links = $this->extractArticleLinksFromHtml($html, $max);
        $res = [];
        foreach ($links as $link) {
            $detail = $this->fetchVietnamPlusArticle($link);
            if ($detail) {
                $res[] = [
                    'title' => $detail['title'] ?? '',
                    'content' => $detail['content'] ?? '',
                    'source_url' => $link,
                    'source_name' => 'VietnamPlus',
                    'publish_time' => $detail['publish_time'] ?? null,
                    'cover_image' => $detail['cover_image'] ?? '',
                    'images' => $detail['images'] ?? []
                ];
            }
            if (count($res) >= $max) break;
        }
        return $res;
    }

    /**
     * 发现RSS链接（从 rss.html 页面）
     */
    protected function discoverRssFeeds(string $rssIndexUrl): array
    {
        $resp = $this->http->get($rssIndexUrl);
        $html = (string)$resp->getBody();
        $dom = new \DOMDocument();
        @$dom->loadHTML($html);
        $xpath = new \DOMXPath($dom);
        $nodes = $xpath->query('//a[@href]');
        $feeds = [];
        foreach ($nodes as $a) {
            $href = $a instanceof \DOMElement ? $a->getAttribute('href') : '';
            if (stripos($href, 'rss') !== false) {
                // 过滤非链接或无效
                if (strpos($href, 'http') !== 0) {
                    // 相对路径
                    $href = rtrim('https://zh.vietnamplus.vn', '/') . '/' . ltrim($href, '/');
                }
                $feeds[] = $href;
            }
        }
        // 去重
        $feeds = array_values(array_unique($feeds));
        // 取前几个即可
        return array_slice($feeds, 0, 5);
    }

    /**
     * 解析RSS items
     */
    protected function parseRssItems(string $xml): array
    {
        libxml_use_internal_errors(true);
        $sx = simplexml_load_string($xml);
        if (!$sx) return [];
        $ns = $sx->channel ?? $sx;
        $items = [];
        foreach ($ns->item ?? [] as $item) {
            $items[] = [
                'title' => trim((string)$item->title),
                'link' => trim((string)$item->link),
                'pubDate' => $this->normalizeDate((string)$item->pubDate),
                'description' => trim((string)$item->description),
            ];
        }
        return $items;
    }

    /**
     * 抓取并解析文章详情
     */
    protected function fetchVietnamPlusArticle(string $url): ?array
    {
        try {
            $resp = $this->http->get($url);
            $html = (string)$resp->getBody();
            return $this->parseVietnamPlusArticleHtml($html, $url);
        } catch (\Throwable $e) {
            Log::channel('ApiResponse')->error('抓取文章失败', [$url, $e->getMessage()]);
            return null;
        }
    }

    protected function parseVietnamPlusArticleHtml(string $html, string $url): array
    {
        $dom = new \DOMDocument();
        @$dom->loadHTML($html);
        $xpath = new \DOMXPath($dom);

        // 标题
        $titleNode = $xpath->query('//h1')?->item(0);
        $title = $titleNode ? trim($titleNode->textContent) : '';

        // 时间（尽量解析）
        $timeNode = $xpath->query('//*[contains(@class, "time") or contains(@class, "date")]')?->item(0);
        $publishTime = $this->normalizeDate($timeNode ? trim($timeNode->textContent) : '');

        // 首图：优先 og:image
        $coverImage = '';
        $ogNode = $xpath->query('//meta[@property="og:image"]')?->item(0);
        if ($ogNode) {
            $coverImage = trim($ogNode instanceof \DOMElement ? $ogNode->getAttribute('content') : '');
        }

        // 内容选择器尝试（多种容器）
        $containers = [
            '//*[contains(@class, "detail") and contains(@class, "content")]//p',
            '//*[contains(@class, "article") and contains(@class, "content")]//p',
            '//*[@id="articleContent"]//p',
            '//article//p',
            '//*[contains(@class, "content") and contains(@class, "main")]//p',
        ];
        $paragraphs = [];
        foreach ($containers as $q) {
            $nodes = $xpath->query($q);
            if ($nodes && $nodes->length > 0) {
                foreach ($nodes as $p) {
                    $text = trim($p->textContent);
                    if ($text !== '') {
                        $paragraphs[] = $text;
                    }
                }
                if (!empty($paragraphs)) break;
            }
        }
        // 若仍为空，抓取所有 p
        if (empty($paragraphs)) {
            $nodes = $xpath->query('//p');
            foreach ($nodes as $p) {
                $text = trim($p->textContent);
                if ($text !== '') {
                    $paragraphs[] = $text;
                }
            }
        }

        // 内容图片提取（文章容器与 figure）
        $imgNodes = $xpath->query('//*[contains(@class, "content") or contains(@id, "article") or name()="article"]//img | //figure//img');
        $images = [];
        if ($imgNodes && $imgNodes->length > 0) {
            foreach ($imgNodes as $img) {
                $src = $this->extractImageSrc($img);
                if ($src) {
                    $images[] = $this->makeAbsoluteUrl($src, 'https://zh.vietnamplus.vn');
                }
            }
            $images = array_values(array_unique($images));
        }
        if (!$coverImage && !empty($images)) {
            $coverImage = $images[0];
        }

        $content = $this->formatTextarea(join("\n\n", $paragraphs));
        return [
            'title' => $title,
            'content' => $content,
            'publish_time' => $publishTime,
            'cover_image' => $coverImage,
            'images' => $images,
            'url' => $url,
        ];
    }

    /** 提取 img 节点的图片地址 */
    protected function extractImageSrc(\DOMElement $img): ?string
    {
        $attrs = ['data-src', 'src', 'data-original', 'data-lazy-src'];
        foreach ($attrs as $a) {
            $v = trim($img->getAttribute($a));
            if (!empty($v)) {
                return $v;
            }
        }
        $srcset = trim($img->getAttribute('srcset'));
        if (!empty($srcset)) {
            $parts = preg_split('/\s*,\s*/', $srcset);
            if (!empty($parts)) {
                $first = explode(' ', $parts[0])[0] ?? '';
                if (!empty($first)) return $first;
            }
        }
        return null;
    }

    /** 生成绝对URL */
    protected function makeAbsoluteUrl(string $u, string $base): string
    {
        if (!$u) return $u;
        if (strpos($u, 'http') === 0) return $u;
        return rtrim($base, '/') . '/' . ltrim($u, '/');
    }

    /** 格式化文本域显示 */
    protected function formatTextarea(string $text): string
    {
        // 统一换行，清理多余空白
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $text = preg_replace("/\r\n|\r/", "\n", $text);
        // 去除不间断空格
        $text = str_replace("\xC2\xA0", ' ', $text);
        // 连续空格压缩
        $text = preg_replace('/[ \t]{2,}/', ' ', $text);
        // 连续3个以上空行压缩为2
        $text = preg_replace("/\n{3,}/", "\n\n", $text);
        // 去掉尾部空白
        return trim($text);
    }

    /** 提取 mostrecent 列表中的文章链接 */
    protected function extractArticleLinksFromHtml(string $html, int $max): array
    {
        $dom = new \DOMDocument();
        @$dom->loadHTML($html);
        $xpath = new \DOMXPath($dom);
        $nodes = $xpath->query('//a[@href]');
        $links = [];
        foreach ($nodes as $a) {
            $href = $a instanceof \DOMElement ? $a->getAttribute('href') : '';
            if (strpos($href, 'http') !== 0) {
                $href = rtrim('https://zh.vietnamplus.vn', '/') . '/' . ltrim($href, '/');
            }
            // 文章详情页通常包含 post 或 article 标识
            if (preg_match('/post\d+\.vnp/i', $href) || preg_match('/article/i', $href)) {
                $links[] = $href;
            }
        }
        $links = array_values(array_unique($links));
        return array_slice($links, 0, $max);
    }

    /** 合并并去重（按 source_url） */
    protected function mergeUnique(array $a, array $b): array
    {
        $map = [];
        foreach ($a as $item) {
            $map[$item['source_url']] = $item;
        }
        foreach ($b as $item) {
            $map[$item['source_url']] = $map[$item['source_url']] ?? $item;
        }
        return array_values($map);
    }

    /** 归一化日期字符串 */
    protected function normalizeDate(?string $str): ?string
    {
        if (!$str) return null;
        $ts = strtotime($str);
        return $ts ? date('Y-m-d H:i:s', $ts) : null;
    }

    // 增加新闻每日获取到的新闻推送给主系统yzt_api（封装到公共yzt客户端）
    protected function pushNewsToApi(array $news): bool
    {
        try {
            $client = new \app\service\http\yzt\article\Article();
            $res = $client->publish($news);
            return isset($res['code']) && (int)$res['code'] === 200;
        } catch (\Throwable $e) {
            Log::channel('ApiResponse')->error('推送新闻到yzt失败', [$e->getMessage(), $news['title'] ?? '']);
            return false;
        }
    }


    /**
     * 同步到 YZT：
     * - 若传入 $saved 为空，则自动查询未同步数据（可选按日期、数量限制）
     * - 对每条进行去重、推送、并记录日志，返回逐条状态
     */
    public function syncToYzt(): array
    {
        $results = [];
        try {
            // 自动查询未同步的数据（在未提供 $saved 时）
            $where = [];
            $candidates = \app\model\news\VnNewsModel::getList($where, ['publish_time' => 'DESC'], '*', 1, 10);
            $saved = [];
            foreach ($candidates as $row) {
                $uniqueKey = isset($row['id']) ? (string)$row['id'] : md5(($row['title'] ?? '') . '|' . ($row['publish_time'] ?? ''));
                $exists = \app\model\news\YztNewsPushLogModel::getOne(['unique_key' => $uniqueKey], ['id']);
                if (!$exists) {
                    $saved[] = $row; // 仅保留未同步的
                }
            }

            $defaultCid = (int) ($cid ?? (config('news.yzt_cid') ?? 10));
            foreach ($saved as $row) {
                $uniqueKey = isset($row['id']) ? (string)$row['id'] : md5(($row['title'] ?? '') . '|' . ($row['publish_time'] ?? ''));
                $exists = \app\model\news\YztNewsPushLogModel::getOne(['unique_key' => $uniqueKey], ['id']);
                if ($exists) {
                    $results[] = ['unique_key' => $uniqueKey, 'skipped' => true, 'reason' => 'duplicate'];
                    // continue;
                }

                $news = [
                    'title' => $row['title'] ?? '',
                    'cid' => (string)$defaultCid,
                    'title_vn' => '',
                    'image' => $row['cover_image'] ?? '',
                    'intro' => $this->filterContent($row['content'] ?? ''),
                    'intro_vn' => '',
                    'sort' => '1',
                    'content' => $row['content'] ?? '',
                    'id' => '',
                    'is_recommend' => '0',
                    'tags' => '',
                    'source' => $row['source_name'] ?? 'VietnamPlus',
                    'table_type' => 'Article',
                    'admin_countries_id' => '2',
                    'is_show' => '1',
                ];

                $ok = $this->pushNewsToApi($news);
                if ($ok) {
                    \app\model\news\YztNewsPushLogModel::add([
                        'unique_key' => $uniqueKey,
                        'news_id' => null,
                        'publish_time' => $this->normalizeDate($row['publish_time'] ?? null),
                        'title' => $row['title'] ?? '',
                        'source' => $row['source_name'] ?? 'VietnamPlus',
                        'tags' => '',
                        'cid' => (string)$defaultCid,
                    ]);
                    $results[] = ['unique_key' => $uniqueKey, 'success' => true];
                } else {
                    $results[] = ['unique_key' => $uniqueKey, 'success' => false];
                }
            }
        } catch (\Throwable $e) {
            Log::channel('ApiResponse')->error('YZT同步失败', [$e->getMessage()]);
        }
        return $results;
    }

    /**
     * 过滤文章内容：移除来源页底部的版权及联系信息
     */
    protected function filterContent(string $text): string
    {
        $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $text = preg_replace("/\r\n|\r/", "\n", $text);
        $patterns = [
            '/^\s*主管部门：.*$/m',
            '/^\s*网站总编辑：.*$/m',
            '/^\s*许可证号：.*$/m',
            '/^\s*电话：.*$/m',
            '/^\s*电子邮件：.*$/m',
            // 版权相关
            '/^\s*©?\s*版权属于.*$/m',
            '/^\s*版权所有.*$/m',
            '/^\s*未经书面同意，禁止任何形式的复制与转载。?$/m',
        ];
        $text = preg_replace($patterns, '', $text);
        // 清理仅包含空白的行
        $text = preg_replace('/^[ \t]+$/m', '', $text);
        // 压缩多余空行
        $text = preg_replace("/\n{3,}/", "\n\n", $text);
        return trim($text);
    }
}
